/*
 * A simple ToDo Manager
 *
 * Copyright (c) 2011 Nosheen Zaza, Manuel Rebollo, Seyedsaeid Mahpeikar and
 * Wo Ke
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package todomanager.model;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.Observable;
import todomanager.data.DataManager;

/**
 * Manages the task retrieval from a data source, and serves as a model in
 * the MVC structure. The purpose of this class is to separate the data access
 * layer from the model representation. So we can change the underlying data 
 * storage without affecting changing the code of the view.
 * This is a singleton class. Since we want all view classes to access it easily
 * through the call {@code TaskManager.getInstance();}. It is more convenient
 * To this application to have a single task manager shared among all views.
 *
 * @author
 * Nosheen Zaza       
 */
public class TaskManager extends Observable {

    private ArrayList<Task> taskList;
    private static TaskManager taskManager;

    /**
     * The constructor is private so that no more than one instance can be
     * created.
     */
    private TaskManager() {
        taskList = new ArrayList<Task>();
    }

    /**
     * Allows access to the only instance of {@code TaskManager}
     * @return taskManager: the only instance of {@code TaskManager}
     */
    public synchronized static TaskManager getInstance() {
        if (taskManager == null) {
            taskManager = new TaskManager();
        }
        return taskManager;
    }

    /**
     * Prevents cloning this singleton class.
     * @return Object
     * @throws CloneNotSupportedException
     */
    @Override
    public Object clone()
            throws CloneNotSupportedException {
        throw new CloneNotSupportedException();

    }

    /**
     * Returns the current task list.
     * @return {@code taskList}
     */
    public ArrayList<Task> getTaskList() {
        return taskList;
    }

    /**
     * Sets {@code taskList} field to the given arguments task list.
     * @param taskList
     */
    public void setTaskList(ArrayList<Task> taskList) {
        this.taskList = taskList;
        setChanged();
        notifyObservers(taskList);
    }

    /**
     * Returns  a task list from a data source, using a {@code DataManager}
     * instance and puts it in the task list, and notifies all observers about
     * this change.
     */
    public void loadAllTasks() {
        taskList = DataManager.getInstance().getAllTasks();
        setChanged();
        notifyObservers(taskList);
    }

    /**
     * Adds a task to the task list and the data source through 
     * {@code DataManager} instance, and notifies all observers about this
     * change.
     * @param text The text content of a task to be added.
     */
    public void addTask(String text, String category, String priority) {
        Task task = new Task(DataManager.getInstance().getTaskCount(), text, category, priority);
        taskList.add(task);
        DataManager.getInstance().addTask(task);
        editTaskId(task);
        setChanged();
        notifyObservers(task);
    }

    /**
     * Adds a task to the task list and the data source through 
     * {@code DataManager} instance, and notifies all observers about this
     * change.
     * @param text
     * @param year
     * @param month
     * @param day
     */
    public void addTask(String text, int year, int month, int day) {
        Task task = new Task(DataManager.getInstance().getTaskCount(),
                text, year, month, day);
        taskList.add(task);
        DataManager.getInstance().addTask(task);
        editTaskId(task);
        setChanged();
        notifyObservers(task);
    }
    
    /**
     * Adds a task to the task list and the data source through 
     * {@code DataManager} instance, and notifies all observers about this
     * change.
     * @param text
     * @param category
     * @param priority
     * @param year
     * @param month
     * @param day
     */
    public void addTask(String text, String category, String priority,
            int year, int month, int day) {
        Task task = new Task(DataManager.getInstance().getTaskCount(), 
                text, category, priority, year, month, day);
        taskList.add(task);
        DataManager.getInstance().addTask(task);
        editTaskId(task);
        setChanged();
        notifyObservers(task);
    }

    /**
     * Removes a task from the task list and the data source through
     * {@code DataManager} instance, and notifies all observers about
     * this change.
     * @param task The task to be removed.
     */
    public void deleteTask(Task task) {
        editTaskId(task);
        DataManager.getInstance().deleteTask(task);
        taskList.remove(task);
        setChanged();
        notifyObservers(null);
    }

    /**
     * Replaces the given task's status with the given status,
     * and notifies all observers about
     * this change.
     * @param task The task which text is to be replaced.
     * @param done The new status.
     */
    public void editTaskStatus(Task task, boolean done) {
        if(task != null) {
            editTaskId(task);
            taskList.get(taskList.indexOf(task)).setDone(done);
            DataManager.getInstance().editTaskStatus(task, done);
            setChanged();
            notifyObservers(task);
        }
    }

    /**
     * Replaces the given task's text with the given text,
     * and notifies all observers about
     * this change.
     * @param task The task which text is to be replaced.
     * @param text The new text.
     */
    public void editTaskText(Task task, String text) {
        if(task != null) {
            editTaskId(task);
            taskList.get(taskList.indexOf(task)).setText(text);
            DataManager.getInstance().editTaskText(task, text);
            setChanged();
            notifyObservers(task);
        }
    }

    /**
     * Replaces a given task's progress with the given one,
     * and notifies all observers about
     * this change.
     * @param task the task to change its progress.
     * @param progress the new progress.
     */
    public void editTaskProgress(Task task, int progress) {
        if (task != null) {
            editTaskId(task);
            taskList.get(taskList.indexOf(task)).setProgress(progress);
            DataManager.getInstance().editTaskProgress(task, progress);
            setChanged();
            notifyObservers(task);
        }
    }

    /**
     * Replaces a given task's category with the given one,
     * and notifies all observers about
     * this change.
     * @param task
     * @param category
     */
    public void editTaskCategory(Task task, String category) {
        if(task != null) {
            editTaskId(task);
            taskList.get(taskList.indexOf(task)).setCategory(category);
            DataManager.getInstance().editTaskCategory(task, category);
            setChanged();
            notifyObservers(task);
        }
    }

    /**
     * Replaces a given task's priority with the given one,
     * and notifies all observers about
     * this change.
     * @param task
     * @param priority
     */
    public void editTaskPriority(Task task, String priority) {
        if(task != null) {
            editTaskId(task);
            taskList.get(taskList.indexOf(task)).setPriority(priority);
            DataManager.getInstance().editTaskPriority(task, priority);
            setChanged();
            notifyObservers(task);
        }
    }

    /**
     * Replaces a given task's date with the given one,
     * and notifies all observers about
     * this change.
     * @param task
     * @param year
     * @param month
     * @param day
     */
    public void editTaskDate(Task task, int year, int month, int day) {
        if (task != null) {
            editTaskId(task);
            taskList.get(taskList.indexOf(task)).setDueDate(new GregorianCalendar(year, month, day));
            DataManager.getInstance().editTaskDate(task, year, month, day);
            setChanged();
            notifyObservers(task);
        }
    }

    /**
     * Removes the due date of the given task.
     * @param task
     */
    public void removeTaskDate(Task task) {
        if (task != null) {
            taskList.get(taskList.indexOf(task)).setDueDate(null);
            DataManager.getInstance().editTaskDate(task, -1, -1, -1);
        }
    }

    /**
     * Sets a task's runtime id.
     * @param t
     */
    private void editTaskId(Task t) {
        t.setId(taskList.indexOf(t));
    }

    /**
     * Removes all tasks, and notifies observers about this change.
     */
    public void deleteAllTasks() {
        taskList = new ArrayList<Task>();
        DataManager.getInstance().deleteAllTasks();
        setChanged();
        notifyObservers();
    }

    /**
     * Saves edit and delete changes to the data source using {@code DataManager}
     * instance.
     */
    public void saveSession() {
        DataManager.getInstance().save();
    }
}
